/*
 * This file is Copyright 2002-2006 Loyola University Chicago,
 * Department of Computer Science and the Emerging Technologies Laboratory.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License via
 *    http://www.apache.org/licenses/LICENSE-2.0.
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.citep.formats.input.accounts;

import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Stack;
import java.util.ArrayList;
import java.math.BigDecimal;
import com.citep.business.Account;
import com.citep.business.AccountTypes;
import com.citep.business.sessions.AccountSession;
import com.citep.business.AccountCategory;
import com.citep.formats.input.ImportException;
import com.citep.formats.input.ImportFileTypes;

public class AccountImportContext implements AccountImportSink {

	public Hashtable<String,ImportedAccount> accountTable = new Hashtable<String,ImportedAccount>();
	private AccountImportStrategy strategy;
	private AccountSession accountSession;

	public AccountImportContext(AccountSession accountSession) {
		this.accountSession = accountSession;
	}
	
	public void onImportAccount(ImportedAccount account) {
		System.out.println("Importing account:" + account);
		
		//if there is no balance defined in the incoming account, set it to 0.00
		if(account.getCurrentBalance() == null)
			account.setCurrentBalance(new BigDecimal("0.00"));
		accountTable.put(account.getImportId(), account);
	}
	
	public void onDocumentEnd() {
		processAccounts();
	}
	
	private void processAccounts() {
//		ArrayList<String> processed = new ArrayList<String>(); //accounts we have already imported
		Stack<String> accountsToProcess = new Stack<String>();
		HashMap<String,Account> processed = new HashMap<String,Account>();

		//FIXME: We should eliminate categories, but in the meantime lets assign the account to a "default" one		
		AccountCategory category = accountSession.getAccountCategory("default");
		if(category == null)
			category = accountSession.createAccountCategory("default", "default category");

		
		Account root = accountSession.getRoot();
		System.out.println("root: " + root);
		if(root == null)
			root = accountSession.createAccount("parent", "parent account", AccountTypes.EQUITY, new BigDecimal("12345"), new Date("1/1/2000"), new Date("1/2/2000"), true, accountSession.createAccountCategory("parent", "parent"));

		
		
		//while there are still accounts to process
		for(String id:accountTable.keySet()) {
			ImportedAccount current = accountTable.get(id);
			boolean done = false;
			
			while(!done) {
				if(!processed.containsKey(current.getImportId())) {
					accountsToProcess.push(current.getImportId());
					processed.put(current.getImportId(), null);
				}
				
				//if this account has a valid parent, set it as current
				//so that it can be imported if it has not been already
				if(current.getImportParentId() != null &&
				   accountTable.containsKey(current.getImportParentId())) {
					current = accountTable.get(current.getImportParentId());
				} else {
					done = true;
				}
			}
			
			
			while(!accountsToProcess.isEmpty()) {
				String accountId = accountsToProcess.pop();
				ImportedAccount account = accountTable.get(accountId);
				if(account != null) {
					//if the account has a parent, then we need to get the parent
					//saved to the database from the hashtable 'processed'
					Account parent = null;
					if(account.getImportParentId() != null) {
						if(processed.containsKey(account.getImportParentId()))
							parent = processed.get(account.getImportParentId());
					} else {
						//if the account has no parent, then the parent
						//has to be the root account
						parent = root;
					}
					
					System.out.println("STORING: " + account);
					System.out.println("(PARENT " + parent + ")");
					
					//save the account in the database
					Account savedAccount = accountSession.createAccount(account.getName(),
							account.getDescription(),
							account.getType(),
							account.getCurrentBalance(),
							null, //startperiod
							null, //endperiod
							true,
							category,
							parent);
					
					//put the newly saved account into the processed hashtable, 
					//so that children accounts can reference it when being stored
					//in the databse
					processed.put(account.getImportId(), savedAccount);
				} else {
					System.out.println("ACCOUNT NOT FOUND");
				}
			}
		}
	}


	public void importFile(String location, ImportFileTypes type ) throws ImportException {
		
		if(type.equals(ImportFileTypes.GNUCASH_ACCOUNTS))
			strategy = new com.citep.formats.input.gnucash.GNUCashAccountsReader();
		else
			throw new ImportException("Unknown file format: " + type.toString());
		
		strategy.setAccountHandler(this);
		strategy.setInputSource(location);
		strategy.start();
	}
	
	public static void main(String [] args) throws ImportException {
		AccountImportContext importContext = new AccountImportContext(null);
		importContext.importFile("/home/lurker/import.gnucash", ImportFileTypes.GNUCASH_ACCOUNTS);
	}
}
